﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Controls.Primitives;
using Bling.DSL;
using Bling.WPF;
using Bling.Util;
using Bling.Physics;
using Bling.Shapes;
using Bling.Geom;

namespace Bling.PhysicsTutorial {
  /// <summary>
  /// Demonstrates how to create a chain using physics. For fun, we use points on the chain to form meta-balls. 
  /// </summary>
  public class Octopus : PhysicsCanvas {
    public Octopus() : base("Octopus", 30) { }
    protected override Action Init(CanvasBl MainCanvas, World World) {
      DoubleBl WidthRatio = .15;
      // Strength of springs between thumbs.
      var Strength = new LabelSliderBl(MainCanvas) {
        Slider = { Minimum = 0, Maximum = 1, Value = 1, },
        Width = MainCanvas.Width, LeftBottom = new PointBl(0, MainCanvas.Height), ZIndex = 100,
        Visibility = true,
        LabelName = "Strength", WidthRatio = WidthRatio,
      };
      // Rest length of springs between thumbs.
      var Length = new LabelSliderBl(MainCanvas) {
        Slider = { Minimum = 1, Maximum = 100, Value = 50, }, 
        Width = MainCanvas.Width, LeftBottom = Strength.LeftTop, ZIndex = 100,
        Visibility = true,
        LabelName = "Length", WidthRatio = WidthRatio,
      };
      // Rest length of springs between thumbs.
      var Straighten = new LabelSliderBl(MainCanvas) {
        Slider = { Minimum = 0, Maximum = 10, Value = 3, }, 
        Width = MainCanvas.Width, LeftBottom = Length.LeftTop, ZIndex = 100,
        Visibility = true,
        LabelName = "Straight", WidthRatio = WidthRatio,
      };
      // 100 thumbs, divided into a nice center
      PositionBlock<Thumb, ThumbBl> block = new PositionBlock<Thumb, ThumbBl>(World, 100, i => {
        return new ThumbBl(MainCanvas) {
          Size = new Point(10, 10), Opacity = 0.2,
          CanDrag = true, Background = i.SelectColor(), ZIndex = 2,
        };
      }) { Name = "Octopus", };
      PointBl Center = block.GetProperty(block.NewBlockProperty<Point>("Center"));
      var centerThumb = new ThumbBl(MainCanvas) {
        Size = new Point(20, 20), Opacity = 0.5, CanDrag = true, Background = Brushes.Black, ZIndex = 3,
      };
      PathShapeBl[] Arms = new PathShapeBl[10];
      for (int i = 0; i < 10; i++) {
        var bi = i * 10;
        Func<int, ThumbBl> ThumbAt = k => {
          ThumbBl ret = block[bi + k].At;
          (((object)ret) != null).Assert();
          return ret;
        };
        Arms[i] = new PathShapeBl(MainCanvas) {
          ZIndex = 1, Stroke = { Thickness = 3, Brush = i.SelectColor() }
        };
        Arms[i].Start = centerThumb;
        Arms[i].Segments.Add(GeometryExtensions.PolygonSegment(1.0, centerThumb, centerThumb, ThumbAt(0), ThumbAt(1)));
        Arms[i].Segments.Add(GeometryExtensions.PolygonSegment(1.0, centerThumb, ThumbAt(0), ThumbAt(1), ThumbAt(2)));
        for (int j = 0; j < 8; j++) {
          var points = new PointBl[4];
          for (int k = 0; k < points.Length; k++) {
            var kk = j + k;
            kk = Math.Min(kk, 9);
            points[k] = ThumbAt(kk);
          }
          Arms[i].Segments.Add(GeometryExtensions.PolygonSegment(1.0, points));
        }
      }
      PointBl r = new Random();
      {
        Center.Link[!centerThumb.IsDragging] = centerThumb.CenterPosition;
        Center.Init = r * new PointBl(500, 500);
        Center.VerletStep(.01);
        Center.Old = Center.Old.Restitution(Center, new Point(0, 0), MainCanvas.Size);
        Center.Bind = Center.Clamp(new Point(0, 0), MainCanvas.Size - new Point(0,0));
      }

      PointBl VirtualCenter = new PointBl(0, 0);
      for (int i = 0; i < 10; i++) {
        VirtualCenter += block[i * 10].Position();
      }
      Center.Relax[.1] = VirtualCenter / 10;


      block.ForAll(body => {
        body.Position().Init = r * new PointBl(500, 500);
        body.Position().Link[!body.At.IsDragging] = body.At.CenterPosition;
        body.Position().VerletStep(.01);

        var snakeId = body.Index / 10;
        var linkId = body.Index % 10;

        if (true) {
          for (int i = 0; i < 5; i++) {
            body.Position().Relax[.25d * Strength.Value] =
              body.Position().Spring((linkId > 0).Condition(body.Prev[1].Position(), Center), Length.Value);
            body.Position().Relax[.25d * Strength.Value, linkId < 10 - 1] =
              body.Position().Spring(body.Next[1].Position(), Length.Value);


            body.Position().Relax[.25d * Strength.Value, linkId > 2] =
              body.Position().Spring(body.Prev[3].Position(), Length.Value * Straighten);
            body.Position().Relax[.25d * Strength.Value, linkId < 10 - 3] =
              body.Position().Spring(body.Next[3].Position(), Length.Value * Straighten);
          }
        }
        body.Position().Old = body.Position().Old.Restitution(body.Position(), new Point(0, 0), MainCanvas.Size);
        body.Position().Bind = body.Position().Clamp(new Point(0, 0), MainCanvas.Size);
      });
      return () => { };
    }
  }
}
    


